﻿using System;
using System.ComponentModel;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using MIXer.Core.Extensions;
using System.IO;
using System.Diagnostics;

namespace MIXer.Core.Domain
{
    public class DownloadFile : INotifyPropertyChanged, IDisposable
    {
        #region Fields
        private WebClient _webClient = new WebClient();
        private bool _downloading;
        private bool _downloaded;
        private int _progress;
        private long _bytesDownloaded;
        private string _bytesDownloadedDesc;
        private string _error;
        private bool _selected;
        private double _speed;
        private string _speedDesc;
        private Stopwatch _watch;
        #endregion

        #region Events
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion

        #region Methods
        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
        public void StartDownloading(string filename, Action progressChanged)
        {
            StartDownloading(filename, null, progressChanged);
        }
        public void StartDownloading(string filename, IWebProxy proxy, Action progressChanged)
        {
            var cts = new CancellationTokenSource();
            var tcs = new TaskCompletionSource<string>(Session.EventCode + Session.SessionCode);

            var ctr = cts.Token.Register(_webClient.CancelAsync);

            _webClient.DownloadFileCompleted += (obj, args) =>
            {
                ctr.Dispose();
                _watch.Stop();

                if (args.Cancelled)
                {
                    tcs.TrySetCanceled();
                    File.Delete(filename);
                }
                else if (args.Error != null)
                {
                    Error = args.Error.Message;
                    tcs.TrySetException(args.Error);
                }
                else
                {
                    Downloaded = true;
                    tcs.TrySetResult(Session.EventCode + Session.SessionCode);
                }

                Downloading = false;
                Progress = 0;

                if (progressChanged != null)
                {
                    progressChanged.Invoke();
                }
            };

            _webClient.DownloadProgressChanged += (obj, args) =>
            {
                Progress = args.ProgressPercentage;
                BytesDownloaded = args.BytesReceived;
                BytesDownloadedDescription = args.BytesReceived.ToFriendlySize();
                Speed = (double)args.BytesReceived / ((double)_watch.ElapsedMilliseconds / 1000D);
                SpeedDescription = Speed.ToFriendlySpeed();

                if (Session.Length == 1)
                {
                    Session.Length = args.TotalBytesToReceive;
                }

                if (progressChanged != null)
                {
                    progressChanged.Invoke();
                }
            };

            Progress = 0;
            BytesDownloaded = 0;
            BytesDownloadedDescription = BytesDownloaded.ToFriendlySize();

            _watch = Stopwatch.StartNew();
            _webClient.Proxy = proxy;
            _webClient.DownloadFileAsync(Session.VideoUrl, filename, tcs);

            Downloading = true;
            IsSelected = false;
        }
        public void Cancel()
        {
            _webClient.CancelAsync();
        }
        public void Dispose()
        {
            _webClient.Dispose();
        }
        #endregion

        public Session Session { get; set; }
        public string OriginalFileName { get; set; }
        public bool IsSelected 
        {
            get { return _selected; }
            set 
            {
                _selected = value;
                NotifyPropertyChanged("IsSelected");
            }
        }
        public int Progress
        {
            get { return _progress; }
            set
            {
                _progress = value;
                NotifyPropertyChanged("Progress");
            }
        }
        public long BytesDownloaded
        {
            get { return _bytesDownloaded; }
            set
            {
                _bytesDownloaded = value;
                NotifyPropertyChanged("BytesDownloaded");
            }
        }
        public double Speed
        {
            get { return _speed; }
            set
            {
                _speed = value;
                NotifyPropertyChanged("Speed");
            }
        }
        public string SpeedDescription
        {
            get { return _speedDesc; }
            set
            {
                _speedDesc = value;
                NotifyPropertyChanged("SpeedDescription");
            }
        }
        public string BytesDownloadedDescription
        {
            get { return _bytesDownloadedDesc; }
            set
            {
                _bytesDownloadedDesc = value;
                NotifyPropertyChanged("BytesDownloadedDescription");
            }
        }
        public bool Downloaded
        {
            get { return _downloaded; }
            set
            {
                _downloaded = value;
                NotifyPropertyChanged("Downloaded");
            }
        }
        public bool Downloading
        {
            get { return _downloading; }
            set
            {
                _downloading = value;
                NotifyPropertyChanged("Downloading");
            }
        }
        public string Error
        {
            get { return _error; }
            set
            {
                _error = value;
                NotifyPropertyChanged("Error");
            }
        }
    }
}
